package com.didd.core.dubbo.file;

import com.didd.core.common.base.*;
import com.didd.core.exception.BizException;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.common.Constants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;
import org.apache.dubbo.rpc.service.GenericService;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.List;
import java.util.Objects;
import java.util.Set;

/**
 * dubbo全局异常处理filter
 *
 * @author diddd
 * @date 2019/6/24
 */
@Slf4j
@Activate(group = Constants.PROVIDER, order = 9999)
public class BizExceptionFilter implements Filter {

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        try {
            return invoker.invoke(invocation);
        } catch (RuntimeException e) {
            log.error("Got unchecked and undeclared exception which called by " + RpcContext.getContext().getRemoteHost()
                    + ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName()
                    + ", exception: " + e.getClass().getName() + ": " + e.getMessage(), e);
            throw e;
        }
    }

    @Override
    public Result onResponse(Result result, Invoker<?> invoker, Invocation invocation) {
        if (result.hasException() && GenericService.class != invoker.getInterface()) {
            try {
                Throwable exception = result.getException();

                // 业务异常返回自定义异常
                if (exception instanceof BizException) {
                    ResultStatus resultStatus;
                    BizException bizException = (BizException) exception;
                    if (Objects.isNull(bizException.getResultStatus())) {
                        resultStatus = new ResultSpecialCode(bizException.getMessage());
                    } else {
                        resultStatus = bizException.getResultStatus();
                    }
                    log.warn("Biz: " + resultStatus.toString(), bizException);
                    return getAppResponse(resultStatus, null);
                }

                // rpc 异常返回自定义异常
                if (exception instanceof RpcException) {
                    RpcException rpcException = (RpcException) exception;
                    ResultStatus resultCode = ResultCode.fetchByCode(rpcException.getCode());
                    log.error("Biz: " + resultCode.toString(), rpcException);
                    return getAppResponse(resultCode, rpcException);
                }

                // hibernate validation 参数校验
                if (exception instanceof ConstraintViolationException) {
                    ConstraintViolationException constraintViolationException = (ConstraintViolationException) exception;
                    Set<ConstraintViolation<?>> set = constraintViolationException.getConstraintViolations();
                    List<Params> paramsList = Lists.newArrayList();
                    set.forEach(constraintViolation -> {
                        Params params = new Params();
                        params.setField(constraintViolation.getPropertyPath().toString());
                        params.setMessage(constraintViolation.getMessage());
                        paramsList.add(params);
                    });
                    log.warn("Biz: {}, {}", ResultCode.FAIL_BIZ_PARAMETER.toString(), paramsList);
                    return getAppResponse(ResultCode.FAIL_BIZ_PARAMETER, paramsList);
                }

                // exception 异常返回未知
                if (exception instanceof Exception) {
                    log.error("Biz: " + ResultCode.FAIL_UNKNOWN.toString(), exception);
                    return getAppResponse(ResultCode.FAIL_UNKNOWN, exception);
                }

            } catch (Throwable e) {
                log.warn("Fail to ExceptionFilter when called by " + RpcContext.getContext().getRemoteHost() + ". services: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName() + ", exception: " + e.getClass().getName() + ": " + e.getMessage(), e);
                return getAppResponse(ResultCode.FAIL_UNKNOWN, e);
            }
        }
        return result;
    }


    /**
     * 返回自定义对象
     *
     * @param status dubbo返回状态枚举接口
     * @param object 具体参数异常
     * @return {@link RpcResult}
     */
    private Result getAppResponse(ResultStatus status, Object object) {
        RpcResponse<?> response = RpcResponse.fail(status, object);
        return new RpcResult(response);
    }

}
